﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.Extensions.Logging;
using Sgr.Security;

/*
 * 1. 基础权限检查：
 *    [CheckPermission("permission")]
 *
 * 2. 严格权限控制（必须同时拥有）：
 *    [CheckPermission("perm1", "perm2", "perm3")]
 *
 * 3. 灵活权限控制（拥有任意一个即可）：
 *    [CheckPermission(PermissionLogicMode.Or, "perm1", "perm2")]
 *
 * 4. 仅认证不验权：
 *    [CheckPermission()]
 *
 * 5. 复杂权限控制：
 *    [CheckPermission(new[] { "perm1", "perm2" }, PermissionLogicMode.And, CheckPermissionResultType.Json)]
 */

namespace Sgr.AspNetCore.ActionFilters.Permissions
{
    /// <summary>
    /// 权限检查特性，用于在控制器方法上声明所需权限
    /// <para>指定该特性只能用于方法，并且允许多次应用在同一方法上</para>
    /// <para>支持与逻辑（AND）和或逻辑（OR）两种权限验证模式</para>
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
    public sealed class CheckPermissionAttribute : Attribute, IAsyncAuthorizationFilter
    {
        // 需要检查的权限名称（支持多个）
        private readonly string[] _permissionSystemNames;

        // 权限检查失败时的返回类型（Json 或 Html）
        private readonly CheckPermissionResultType _resultType;

        // 权限逻辑运算模式
        private readonly PermissionLogicMode _logicMode;

        /// <summary>
        /// 无参构造函数：仅进行认证检查
        /// </summary>
        public CheckPermissionAttribute()
            : this(Array.Empty<string>(), PermissionLogicMode.And, CheckPermissionResultType.Json)
        {
        }

        /// <summary>
        /// 单权限构造函数
        /// </summary>
        /// <param name="permissionSystemName">权限系统名称</param>
        public CheckPermissionAttribute(string permissionSystemName)
            : this(new[] { permissionSystemName }, PermissionLogicMode.And, CheckPermissionResultType.Json)
        {
            if (string.IsNullOrWhiteSpace(permissionSystemName))
                throw new ArgumentException("权限名称不能为空", nameof(permissionSystemName));
        }

        /// <summary>
        /// 多权限构造函数（可变参数，与逻辑）
        /// </summary>
        /// <param name="permissionSystemNames">权限系统名称数组</param>
        public CheckPermissionAttribute(params string[] permissionSystemNames)
            : this(permissionSystemNames, PermissionLogicMode.And, CheckPermissionResultType.Json) // ✅ 与逻辑
        {
        }

        /// <summary>
        /// 便利构造函数：支持显式指定逻辑模式的可变参数版本
        /// </summary>
        /// <param name="logicMode">权限逻辑运算模式</param>
        /// <param name="permissionSystemNames">权限系统名称数组</param>
        public CheckPermissionAttribute(PermissionLogicMode logicMode, params string[] permissionSystemNames)
            : this(permissionSystemNames, logicMode, CheckPermissionResultType.Json)
        {
        }

        /// <summary>
        /// 构造函数：指定多个权限名称和逻辑模式
        /// </summary>
        /// <param name="permissionSystemNames">权限系统名称数组</param>
        /// <param name="logicMode">权限逻辑运算模式</param>
        /// <param name="resultType">权限不足时的返回类型</param>
        /// <exception cref="ArgumentException">权限名称数组不能为空或包含空字符串</exception>
        public CheckPermissionAttribute(string[] permissionSystemNames,
            PermissionLogicMode logicMode,
            CheckPermissionResultType resultType)
        {
            ValidatePermissionNames(permissionSystemNames);
            _permissionSystemNames = permissionSystemNames;
            _logicMode = logicMode;
            _resultType = resultType;
        }

        /// <summary>
        /// 验证权限名称数组
        /// </summary>
        private static void ValidatePermissionNames(string[] permissionSystemNames)
        {
            if (permissionSystemNames == null)
                throw new ArgumentException("权限名称数组不能为空", nameof(permissionSystemNames));
            if (permissionSystemNames.Any(string.IsNullOrWhiteSpace))
                throw new ArgumentException("权限名称不能为 null 或空字符串", nameof(permissionSystemNames));
        }

        public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            try
            {
                // 1. 先检查是否已认证
                if (context.HttpContext.User.Identity is not { IsAuthenticated: true })
                {
                    LogPermissionCheck(context, _permissionSystemNames, _logicMode, false, "用户未认证");
                    var problemDetails = context.HttpContext.CreateUnauthorizedProblemDetails();
                    context.Result = new UnauthorizedObjectResult(problemDetails);
                    return;
                }

                // 2. 如果没有指定权限，则认证通过即可
                if (_permissionSystemNames.Length == 0)
                {
                    //LogPermissionCheck(context, _permissionSystemNames, _logicMode, true, "无需权限验证");
                    return;
                }

                // 3. 根据逻辑模式进行权限检查
                var functionPermissionGrantingService = context.HttpContext.RequestServices
                    .GetRequiredService<IFunctionPermissionGrantingService>();

                bool hasPermission = await CheckPermissionsByLogicMode(
                    functionPermissionGrantingService,
                    context.HttpContext.User,
                    _permissionSystemNames,
                    _logicMode);

                if (hasPermission)
                {
                    //LogPermissionCheck(context, _permissionSystemNames, _logicMode, true, "权限验证通过");
                    return;
                }

                LogPermissionCheck(context, _permissionSystemNames, _logicMode, false, "权限验证失败");

                // 4. 返回权限不足响应
                context.Result = _resultType switch
                {
                    CheckPermissionResultType.Json => CreateAccessDeniedJsonResult(context.HttpContext, _permissionSystemNames, _logicMode),
                    CheckPermissionResultType.Html => CreateAccessDeniedViewResult(),
                    _ => new ForbidResult()
                };
            }
            catch (Exception ex)
            {
                context.HttpContext.RequestServices
                    .GetService<ILogger<CheckPermissionAttribute>>()
                    ?.LogError(ex, "权限检查过程中发生异常");

                context.Result = new StatusCodeResult(StatusCodes.Status500InternalServerError);
            }
        }

        /// <summary>
        /// 根据逻辑模式检查权限
        /// </summary>
        public static async Task<bool> CheckPermissionsByLogicMode(
            IFunctionPermissionGrantingService service,
            System.Security.Claims.ClaimsPrincipal user,
            string[] permissions,
            PermissionLogicMode logicMode)
        {
            return logicMode switch
            {
                PermissionLogicMode.Or => await CheckPermissionsWithOrLogic(service, user, permissions),
                PermissionLogicMode.And => await CheckPermissionsWithAndLogic(service, user, permissions),
                _ => throw new ArgumentOutOfRangeException(nameof(logicMode), logicMode, "不支持的权限逻辑模式")
            };
        }

        /// <summary>
        /// 或逻辑权限检查：只要有一个权限通过即视为通过
        /// </summary>
        private static async Task<bool> CheckPermissionsWithOrLogic(
            IFunctionPermissionGrantingService service,
            System.Security.Claims.ClaimsPrincipal user,
            string[] permissions)
        {
            foreach (string permission in permissions)
            {
                if (await service.IsGrantedAsync(user, permission))
                    return true;
            }
            return false;
        }

        /// <summary>
        /// 与逻辑权限检查：必须拥有所有权限才能通过
        /// </summary>
        private static async Task<bool> CheckPermissionsWithAndLogic(
            IFunctionPermissionGrantingService service,
            System.Security.Claims.ClaimsPrincipal user,
            string[] permissions)
        {
            foreach (string permission in permissions)
            {
                if (!await service.IsGrantedAsync(user, permission))
                    return false;
            }
            return true;
        }

        public static ObjectResult CreateAccessDeniedJsonResult(HttpContext httpContext, string[] requiredPermissions, PermissionLogicMode logicMode)
        {
            var logicDescription = logicMode == PermissionLogicMode.And ? "需要同时拥有所有权限" : "需要拥有其中任意一个权限";

            var problemDetails = new ProblemDetails
            {
                Status = StatusCodes.Status403Forbidden,
                Title = "您暂无权限访问此功能",
                Type = ProblemTypes.Forbidden,
                Instance = httpContext.Request.Path
            };

            // 添加详细错误信息
            problemDetails.Extensions["sgr_errors"] = new Dictionary<string, object>
            {
                ["requiredPermissions"] = requiredPermissions,
                ["logicMode"] = logicMode.ToString(),
                ["logicDescription"] = logicDescription,
                ["currentUser"] = httpContext.User.Identity?.Name ?? "",
                ["timestamp"] = DateTime.UtcNow
            };

            return new ObjectResult(problemDetails)
            {
                StatusCode = StatusCodes.Status403Forbidden,
                ContentTypes = { "application/problem+json" }
            };
        }

        public static ViewResult CreateAccessDeniedViewResult()
        {
            var viewResult = new ViewResult
            {
                ViewName = "~/Views/Shared/AccessDenied.cshtml",
                StatusCode = StatusCodes.Status403Forbidden,
                ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
                {
                    { "Title", "访问被拒绝" },
                    { "Message", "您暂无权限访问此功能" },
                    { "ReturnUrl", "/" }
                }
            };

            return viewResult;
        }

        private static void LogPermissionCheck(AuthorizationFilterContext context, string[] permissions, PermissionLogicMode logicMode, bool isGranted, string reason)
        {
            var logger = context.HttpContext.RequestServices.GetService<ILogger<CheckPermissionAttribute>>();
            if (logger == null) return;

            var user = context.HttpContext.User.Identity?.Name ?? "anonymous";
            var path = context.HttpContext.Request.Path;
            var method = context.HttpContext.Request.Method;
            var logicModeText = logicMode == PermissionLogicMode.And ? "AND" : "OR";

            if (!isGranted)
            {
                logger.LogWarning(
                    "用户 {User} {Method} {Path} 权限检查失败。需要权限: {Permissions}，逻辑模式: {LogicMode}，失败原因: {Reason}",
                    user, method, path, string.Join(", ", permissions), logicModeText, reason);
            }
            else
            {
                logger.LogInformation(
                    "用户 {User} {Method} {Path} 权限检查通过。逻辑模式: {LogicMode}，原因: {Reason}",
                    user, method, path, logicModeText, reason);
            }
        }
    }
}